#include <cassert>
#include <random>
#include <nice2d/distribution.h>

namespace
{
std::mt19937 randomEngine;
}

int random(int min, int max)
{
    assert(min <= max);
    std::uniform_int_distribution<int> distribution(min, max);
    return distribution(randomEngine);
}

uint32_t random(uint32_t min, uint32_t max)
{
    assert(min <= max);
    std::uniform_int_distribution<uint32_t> distribution(min, max);
    return distribution(randomEngine);
}

float random(float min, float max)
{
    assert(min <= max);
    std::uniform_real_distribution<float> distribution(min, max);
    return distribution(randomEngine);
}

float randomDev(float middle, float deviation)
{
    assert(deviation >= 0.f);
    return random(middle - deviation, middle + deviation);
}

template <typename T>
Distribution<T> uniformT(T min, T max)
{
    assert(min <= max);

    return [ = ] () -> T {
        return random(min, max);
    };
}

Distribution<int> uniform(int min, int max)
{
    return uniformT(min, max);
}

Distribution<uint32_t> uniform(uint32_t min, uint32_t max)
{
    return uniformT(min, max);
}

Distribution<float> uniform(float min, float max)
{
    return uniformT(min, max);
}

Distribution<sf::Time> uniform(const sf::Time &min, const sf::Time &max)
{
    assert(min <= max);

    const float floatMin = min.asSeconds();
    const float floatMax = max.asSeconds();

    return [=]() -> sf::Time {
        return sf::seconds(random(floatMin, floatMax));
    };
}

Distribution<sf::Vector2f> rect(const sf::Vector2f &center, const sf::Vector2f &halfSize)
{
    assert(halfSize.x >= 0.f && halfSize.y >= 0.f);

    return [ = ] () -> sf::Vector2f {
        return sf::Vector2f(
            randomDev(center.x, halfSize.x),
            randomDev(center.y, halfSize.y));
    };
}

Distribution<sf::Vector2f> circle(const sf::Vector2f &center, float radius)
{
    assert(radius >= 0.f);

    return [ = ] () -> sf::Vector2f {
        float r = random(0.0f, 1.0f) * radius;
        float angle = random(0.0f, 360.0f);
        angle = angle * M_PI / 180.0f;
        float x = r * std::cos(angle);
        float y = r * std::sin(angle);
        return center + sf::Vector2f(x, y);
    };
}
